<?php
/**
 * Created by PhpStorm.
 * User: tom_xia
 * Date: 2017/11/30
 * Time: 11:43
 * note:适用场景 异步上传.支持 单文件上传，多文件上传 单文件分片上传
 */
require_once "FileConfigException.php";
abstract class AbstractUpload
{
    const UPLOAD_INDEX = 'UPLOAD_UNIQUE_INDEX';

    //基本上传文件配置
    protected $formName = "file";//上传文件时，文件表单的名字
    protected $uploadPath;//保存路径,1.如果不分片，这个就是最终的保存路径，2.如果分片，这就是临时保存路径
    protected $saveHttp;//访问http base url
    protected $allowType = "*";//允许上传类型用逗号分割，*号代表全部，例如*和jpg,jpeg,gif

    /**
     * @var $redis \Redis
     */
    protected $redis;//redis对象


    protected $files =[];//上传的文件信息 在init中被初始化

    /**
     * AbstractUpload constructor.
     * @param array $config
     * @throws FileUploadException
     * @param
     */
    public function __construct(array $config)
    {
        if(!array_key_exists("uploadpath",$config)){
            throw new FileUploadException("缺少文件保存路径");
        }
        if(!array_key_exists("redis",$config)){
            throw new FileUploadException("缺少redis对象，请生成reids以便高并发时候产生唯一文件名称");
        }
        if(!array_key_exists("savehttp",$config)){
            throw new FileUploadException("缺少文件http访问路径");
        }

        $this->redis = $config["redis"];
        $this->uploadPath = $config["uploadpath"];
        $this->saveHttp = $config["savehttp"];

        $this->allowType = isset($config["allowtype"]) ? $config["allowtype"] :"*";
    }

    /**
     * 获取文件的扩展名称
     * @param $fileName
     */
    public function getFileExt(array $fileInfo)
    {
        return   pathinfo($fileInfo["name"],PATHINFO_EXTENSION);
    }

    protected $size = [];//上传文件的大小

    protected function init()
    {
        if(!isset($_FILES[$this->formName]) || !isset($_FILES[$this->formName]["name"])){
            throw new FileUploadException("上传的文件不能为空:【$this->formName】");
        }
        if(is_array($_FILES[$this->formName]["name"])){
            foreach($_FILES[$this->formName]["name"] as $key=>$name){
                $arr = [];
                $arr["name"] = $_FILES[$this->formName]["name"][$key];
                $arr["size"] = $_FILES[$this->formName]["size"][$key];
                $arr["tmp_name"] = $_FILES[$this->formName]["tmp_name"][$key];
                $arr["type"] = $_FILES[$this->formName]["type"][$key];
                $arr["error"] = $_FILES[$this->formName]["error"][$key];
                $this->files[] = $arr;
            }
        }else{
            $this->files[] = $_FILES[$this->formName];
        }
        $this->uploadPath = rtrim($this->uploadPath,"/");

        foreach ($this->files as &$fileInfo){
            $errorCode = $fileInfo["error"];
            if($errorCode == 0) {
                $isPostUpload = is_uploaded_file($fileInfo["tmp_name"]);
                if (!$isPostUpload) {
                    throw new FileUploadException("文件不安全");
                }
                //检查文件类型
                $isAllowType = $this->checkFileType($fileInfo);
                if (!$isAllowType) {
                    throw new FileUploadException("不允许上传的文件类型,允许上传的文件类型是:{$this->allowType}");
                }
                $this->size[] = $fileInfo["size"];
            }else{
                $this->setErrorByCode($errorCode);
            }
        }
    }

    public function mkdir($filePath='')
    {
        if(empty($filePath)){
            $filePath = $this->uploadPath;
        }
        if(!file_exists($filePath)){
           return  mkdir($filePath,0744,true);
        }else{
            return true;
        }
    }

    /**
     * 文件保存的 后缀路径
     * @var
     */
    protected $savePathSuffix ;

    public function getSavePathSuffix()
    {
        if(!empty($this->savePathSuffix)){
            return $this->savePathSuffix;
        }
        $suffix = date("Y/m/d");
        $this->savePathSuffix = $suffix;
        return $suffix;
    }

    /**
     * 获取保存文件的key,用户md5加密使用
     * @return string
     */
    protected function getSaveFileKey(){
        return time()."_".mt_rand(1000,9999);
    }
    /**
     * 上传
     * @return bool
     */
    abstract  public function doupload();

    protected function moveFileToSavePath($source,$destination)
    {
        $moveSuccess = move_uploaded_file($source,$destination);
        if(!$moveSuccess){
            return false;
        }
        return true;
    }
    /**
     * @desc上传文件的大小
     * @return mixed
     */
    public function getSize()
    {
        return $this->size;
    }
    /**
     * @desc 设置错误码的错误信息
     * @param $errCode 错误码
     */
    public function setErrorByCode($errCode)
    {
        switch($errCode)
        {
            case 1:
                $max_size = ini_get("upload_max_filesize");
                throw new FileUploadException("文件上传大小超过限制:{$max_size}");
                break;
            case 2:
                throw new FileUploadException("超过表单上传大小");
                break;
            case 3:
                throw new FileUploadException("部分文件上传成功");
                break;
            case 4:
                throw new FileUploadException("没有文件被上传");
                break;
            case 6:
                throw new FileUploadException("找不到上传的临时文件");
                break;
            case 7:
                throw new FileUploadException("写入临时文件失败");
                break;
        }
    }

    /**
     * @desc 检查文件是否是允许上传的类型
     * @return bool
     */
    protected function checkFileType(array $fileInfo)
    {
        if($this->allowType === "*"){
            return true;
        }else{
            $allowType = explode(",",$this->allowType);
            $fileExt = $this->getFileExt($fileInfo);
            if(in_array($fileExt,$allowType)){
                return true;
            }else{
                return false;
            }
        }
    }

    protected $finalFileName=[];//合并文件之后生成的文件名称,最终文件存放名称。
    protected $fileUrl = [];//文件访问url
    public function getFileUrl(){
        return $this->fileUrl;
    }
    /**
     * @desc 获取上传文件的最终文件名称，包括后缀
     * @return array
     */
    public function getFinalFileName(){
        return $this->finalFileName;
    }

    /**
     * 获取上传之后的信息
     * @return mixed
     */
    public abstract  function getFileDataInfo();
}